package com.xuzy.x808.server.handler;

import com.xuzy.x808.common.utils.ByteUtils;
import com.xuzy.x808.entity.MsgHeader;
import com.xuzy.x808.entity.PackageData;
import com.xuzy.x808.server.clients.Connections;
import com.xuzy.x808.server.processor.ProcessServiceRegister;
import com.xuzy.x808.server.protocol.parser.MsgParser;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
@ChannelHandler.Sharable
public class ServerHandler extends SimpleChannelInboundHandler<ByteBuf> {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private ProcessServiceRegister processServiceRegister;

    /**
     * 要求 13  12-10 都是0，如果不是，则直接不处理
     *
     * @param property
     * @return
     */
    private static Integer decodeMsgSplit(String property) {
        String format = String.format("%16s", property);
        String replace = format.replace(" ", "0");
        String substring = replace.substring(2, 3);
        return Integer.valueOf(substring);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
        if (byteBuf.readableBytes() < 1) {
            return;
        }

        logger.info("数据读入:" + ByteBufUtil.hexDump(byteBuf));
        byteBuf = MsgParser.restore0x7D(byteBuf);
        if (byteBuf == null) {
            return;
        }
        //step1 解析协议报文
        PackageData packageData = parseMsg(byteBuf);
        if (packageData == null) {
            return;
        }

        //step2 按msgId不同，调相应处理器处理消息
        processServiceRegister.getProcessor(packageData.getMsgHeader().getMsgId()).process(ctx, packageData);
    }

    /**
     * [0,1]是msgId，[2,3]是property，short占两个字节，所以此处可以直接取而不用指定起始位置和获取长度
     *
     * @param byteBuf
     */
    private PackageData parseMsg(ByteBuf byteBuf) {
        Integer msgId = byteBuf.getUnsignedShort(0);
        String property = Integer.toBinaryString(byteBuf.getUnsignedShort(2));
        Integer msgLength = decodeMsgLength(property);
        //如果是分包和数据加密的，直接不处理
        Integer msgSplit = decodeMsgSplit(property);
        if (msgSplit > 0) {
            return null;
        }
        //设备ID
        String devId = ByteUtils.bcdToString(ByteBufUtil.getBytes(byteBuf, 4, 6));
        //消息流水号
        Integer seq = byteBuf.getUnsignedShort(10);
        //按照协议：“如果消息体属性中相关标识位确定消息分包处理， 则该项有内容，否则无该项” 此处无分包，所以消息体直接从位置12开始，读msgLength个长度
        ByteBuf msgBody = byteBuf.copy(12, msgLength);

        return buildPackageData(msgId, property, msgLength, devId, seq, msgBody);
    }

    private PackageData buildPackageData(Integer msgId, String property, Integer msgLength, String devId, Integer seq, ByteBuf msgBody) {
        MsgHeader msgHeader = MsgHeader.builder().msgId(msgId).msgBodyProps(property).msgBodyLength(msgLength).devId(devId).seq(seq).build();

        PackageData packageData = PackageData.builder().msgHeader(msgHeader).msgBody(msgBody).build();
        return packageData;
    }

    /**
     * 消息体属性property共16位：
     * 15-14 13  12-10     9-0
     * 保留 分包 数据加密方式 消息体长度
     * 拿到字节后，格式化为16位，不足的高位补0，从位置9开始，是消息体长度，然后转10进制输出
     *
     * @param property
     * @return
     */
    private Integer decodeMsgLength(String property) {
        String format = String.format("%16s", property);
        String replace = format.replace(" ", "0");
        String substring = replace.substring(6);
        return Integer.valueOf(substring, 2);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        logger.info("客户端连接:" + ctx.channel().id().toString());
    }

    /**
     * 设备正常断连（客户端或服务端调用断连方法），要做出的处理
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Connections.removeClient(ctx.channel().id().toString());

        String devId = Connections.getDevId(ctx.channel().id().toString());
        logger.warn("设备断开连接，设备Id：{}", devId);

        super.channelInactive(ctx);
    }

    /**
     * 设备非正常断连（客户端或服务端未调用断连方法，但心跳超时了），也同样将设备的连接从connections里移除
     *
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
            if (idleStateEvent.state() == IdleState.ALL_IDLE) {
                Connections.removeClient(ctx.channel().id().toString());

                String devId = Connections.getDevId(ctx.channel().id().toString());
                logger.error("设备超时断开，设备Id：{}", devId);

                ctx.close();
            }
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }
}
